<!DOCTYPE html>
<html lang="zh-CN">

<!-- Head tag -->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  
  <meta name="baidu-site-verification" content="undefined" />
  <meta name="google-site-verification" content="undefined" />
  <meta name="msvalidate.01" content="undefined" />
  <meta name="360-site-verification" content="undefined" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- if page has tags, then add tags to keyword -->
  
  
  <meta name="keywords" content=",MySQL,">
  <!-- page.description has higher priority -->
  <meta name="description" content="高性能MySQL读后感，索引基础部分">
  <link rel="shortcut icon" href="/blog/img/logo.webp">
  <title>
    
    《高性能MySQL》笔记-索引基础 | Evrn
    
  </title>

  <link rel="canonical" href="/blog/post/mysql-index-base.html">
  
<link rel="stylesheet" href="/blog/css/reset.css">

  
<link rel="stylesheet" href="/blog/css/layout.css">

  
<link rel="stylesheet" href="/blog/css/markdown.css">

  <!-- Pygments Highlight CSS -->
  
<link rel="stylesheet" href="/blog/css/highlight.css">

  
<link rel="stylesheet" href="/blog/css/geektutu.css">

  <!-- global function -->
  <script>
    window.globalAddScript = function (url, onload, onerror) {
      var s = document.createElement('script');
      s.src = url;
      onload && (s.onload = onload);
      onerror && (s.onerror = onerror);
      document.body.appendChild(s);
    }
    window.globalAddCss = function (url) {
      var s = document.createElement('link');
      s.rel = 'stylesheet';
      s.href = url;
      document.body.appendChild(s);
    }
    window.getPosition = function (ele) {
      var x = 0, y = 0;
      while (ele) {
        x += (ele.offsetLeft - ele.scrollLeft + ele.clientLeft);
        y += (ele.offsetTop - ele.scrollTop + ele.clientTop);
        ele = ele.offsetParent;
      }
      return { x: x, y: y };
    }
    window.getDom = function (str) { return document.querySelector(str) }
  </script>
  <!-- google ad -->
  
<meta name="generator" content="Hexo 4.2.0"></head>

<body>
    <header class="gkt-header col-xs-12 padding-0">
    <div id="gkt-nav" class="gkt-header-container">
        <a href="/blog/" class="gkt-header-title float-left">
            <img class="float-left" src="/blog/img/logo.webp" alt="">
            <span>Evrn</span>
        </a>
        <nav class="gkt-header-nav text-right">
            <ul>
                <li><a class="hidden-xs" href="/blog/">主页</a></li>
                <li><a href="/blog/series/">专题</a></li>
                <li><a href="/blog/archives/">归档</a></li>
                <li><a href="/blog/post/link.html">友链</a></li>
                <li><a href="/blog/post/about.html">留言</a></li>
            </ul>
        </nav>
    </div>
    <div id="gkt-cate-nav" class="gkt-header-container hidden-xs">
        
        <nav class="gkt-header-nav float-left">
            <ul>
                
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-learn-interface.html">Golang学习</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-asm-indroduce.html">Golang汇编</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/tcp-three-way-handshake.html">基础</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-how-to-build.html">Golang底层</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left active">
                    <a class="float-left" href="/blog/post/mysql-index-use.html">MySQL</a>
                    
                </li>
                
            </ul>
        </nav>
    </div>
</header>
<div style="height: 44px"></div>
<div class="hidden-xs" style="height: 44px"></div>
<script>
    (function () {
        window.addEventListener('scroll', function () {
            if (window.innerWidth < 768) {
                return;
            }
            var nav = document.querySelector('#gkt-nav');
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            scrollTop > 50 && (nav.classList.add('hide'));
            scrollTop <= 50 && (nav.classList.remove('hide'));
        });
        var cateNavs = document.querySelectorAll('#gkt-cate-nav>nav>ul>li');
        [].slice.call(cateNavs).forEach(function (item) {
            var sub = item.querySelector('.gkt-sub-cate');
            if (!sub) return;
            item.addEventListener('mouseenter', function (e) { sub.style.display = 'block'; }, false);
            item.addEventListener('mouseleave', function (e) { sub.style.display = 'none'; }, false);
        })
    })();
</script>
    <!-- Main Content -->
    <div class="main-container">
        <!-- Main Content -->
<main class="col-xs-12 padding-0 markdown-it">
    <!-- Post Container -->
    
    
    <!-- Post Content -->
<div class="float-left post-container box-shadow">
    <div class="u-arrow-wrapper hidden-xs">
        
        <a class="float-left" href="/blog/post/go-how-to-build.html"><i class="u-arrow-left"></i></a>
        
        
        <a class="float-right" href="/blog/post/mysql-optimize-explain.html"><i class="u-arrow-right"></i></a>
        
    </div>
    <article class="col-xs-12">
        <h1> 《高性能MySQL》笔记-索引基础 </h1>

        

        
        <div class="hidden-lg hidden-md series_links">
            <p> <strong> MySQL系列文章链接：</strong></p>
            <ul>
                
                <li>
                    <a href="/blog/post/mysql-index-use.html">《高性能MySQL》笔记-索引优化</a>
                    <span class="post-item-date">(Mar 1, 2020)</span>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html">《高性能MySQL》笔记-索引基础</a>
                    <span class="post-item-date">(Mar 2, 2020)</span>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html">EXPLAIN-执行计划查询命令</a>
                    <span class="post-item-date">(Mar 3, 2020)</span>
                </li>
                
            </ul>
        </div>
        
        
        
        <h2 id="索引简介"><a href="#索引简介" class="headerlink" title="索引简介"></a>索引简介</h2><p>MySQL服务器的逻辑架构</p>
<p><img src="http://river.marvelthanos.com/blog/20200325/guo5DVde5tzI.png?imageslim" alt="mark"></p>
<p>索引(MySQL中也叫做“key”)是<strong>存储引擎</strong>用于快速找到记录的一种<strong>数据结构</strong>，类似于书本的<strong>目录</strong>。</p>
<p>不同的存储引擎的索引工作方式不一样，也不是所有的存储引擎都支持所有类型的索引。</p>
<p><strong>索引可以包含一个或多个列的值</strong>，如果索引包含多个列的值，则<strong>列的顺序</strong>也十分重要，因为MySQL只能高效地使用索引的<strong>最左前缀列</strong>。</p>
<p>创建一个包含两个列的索引和创建两个只包含一列的索引是不一样的。</p>
<h2 id="索引的类型"><a href="#索引的类型" class="headerlink" title="索引的类型"></a>索引的类型</h2><h3 id="哈希索引Hash"><a href="#哈希索引Hash" class="headerlink" title="哈希索引Hash"></a>哈希索引Hash</h3><p>哈希索引是基于哈希表实现的，只有精确匹配索引所有列的查询才有效。对于每行数据，存储引擎都会对所有的索引列计算一个哈希码，为了减少空间占用哈希码通常是一个较小的值</p>
<p>因此必然出现冲突，这时候就通过链表来存放多条记录。</p>
<h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h4><p>1、hash索引是基于hash表实现的，只有查询条件精确匹配hash索引中的所有列的时候，才能用到hash索引。</p>
<p>2、对于hash索引中的所有列，存储引擎都会为每一行计算一个hash码，hash索引中存储的就是hash码。</p>
<p>3、hash索引包括键值、hash码和指针 。</p>
<p>　　因为hash索引本身只需要存储对应的hash值，所以索引的结构十分紧凑，这也让hash索引查找的速度非常快。然而，hash索引也是存在其限制的：</p>
<h4 id="限制"><a href="#限制" class="headerlink" title="限制"></a>限制</h4><p>1、hash索引必须进行二次查找<br>使用hash索引两次查找，第一次找到相应的行，第二次读取数据，但是被频繁访问到的行一般会缓存在内存中，这点对数据库性能的影响不大。 　<br>2、hash索引不能用于外排序<br>hash索引存储的是hash码而不是键值，所以无法用于外排序 　<br>3、hash索引不支持部分索引查找也不支持范围查找<br>只能用到等值查询，不能范围和模糊查询<br>4、hash索引中的hash码的计算可能存在hash冲突<br>当出现hash冲突的时候，存储引擎必须遍历整个链表中的所有行指针，逐行比较，直到找到所有的符合条件的行，若hash冲突很多的话，一些索引的维护代价机会很高，所以说hash索引不适用于选择性很差的列上（重复值很多）。姓名、性别、身份证（合适） </p>
<p>5、哈希索引只包含哈希值和行指针，而不存储字段值，所以不能使用索引中的值来避免读取行。</p>
<p>6、如果哈希冲突很多的话，一些索引维护操作的代价也会很高</p>
<h4 id="自适应hash索引"><a href="#自适应hash索引" class="headerlink" title="自适应hash索引"></a>自适应hash索引</h4><p>hash索引一般用于内存数据库，比如memory存储引擎，数据放到内存里，为了加快速度，创建hash索引。　　</p>
<p>其实也有一个叫“自适应hash索引”的东西。当InnoDB注意到某些索引值被使用的非常频繁时，它会在内存中基于B-Tree索引上在创建一个hash索引，这样就让B-tree索引也具有hash索引的一些优点。这是一个完全自动的内部的行为，用户无法控制或配置，不过，如果有需要，完全可以关闭该功能。</p>
<p>另外我们如果需要也可以自定义哈希索引，比如我们要精确匹配url的记录，可以先新建一个列专门存放hash值url_crc </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alter table t add column url_crc int UNSIGNED NOT NULL DEFAULT 0;</span><br></pre></td></tr></table></figure>

<p>然后写一个触发器，每次插入或者更新的时候自动更新hash索引列</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">CREATE TRIGGER t BEFORE INSERT ON t FOR EACH ROW </span><br><span class="line">BEGIN	</span><br><span class="line">SET NEW.url_crc&#x3D;CRC32(NEW.url);</span><br><span class="line">END;</span><br><span class="line">CREATE TRIGGER t BEFORE UPDATE ON t FOR EACH ROW </span><br><span class="line">BEGIN	</span><br><span class="line">SET NEW.url_crc&#x3D;CRC32(NEW.url);</span><br><span class="line">END;</span><br></pre></td></tr></table></figure>

<table>
<thead>
<tr>
<th align="center">url</th>
<th align="center">url_crc</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a href="http://www.baidu.com" target="_blank" rel="noopener">http://www.baidu.com</a></td>
<td align="center">3500265894</td>
</tr>
</tbody></table>
<p>查询的时候同样也要使用对应的哈希方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from t where url_crc&#x3D;CRC32(&quot;http:&#x2F;&#x2F;www.baidu.com&quot;)</span><br></pre></td></tr></table></figure>

<p>注意这里哈希方法是CRC32生成的位数很少，冲突会很大这里只是为了举例，实际应用可以根据需求自定义哈希方法，比如书上建议的</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT CONV(RIGHT(MD5(&quot;http:&#x2F;&#x2F;www.baidu.com&quot;),16),16,10) AS HASH64</span><br></pre></td></tr></table></figure>

<h3 id="空间索引（R-Tree）"><a href="#空间索引（R-Tree）" class="headerlink" title="空间索引（R-Tree）"></a>空间索引（R-Tree）</h3><p>MyISAM表支持空间索引，可以用作地理数据存储，开源数据库系统中对GIS的解决方案做得比较好的是PostgreSQL的PostGIS</p>
<h3 id="全文索引"><a href="#全文索引" class="headerlink" title="全文索引"></a>全文索引</h3><p>查找文本中的关键词，不是直接比较索引中的值。适用于MATCH AGAINST操作，而不是普通的WHERE条件操作</p>
<h3 id="B-Tree"><a href="#B-Tree" class="headerlink" title="B-Tree"></a>B-Tree</h3><p>MySQL最常用的两个存储引擎MyISAM,InnoDB都用的B-Tree索引，不同的存储引擎使用B-Tree索引<strong>性能会有不同</strong>，各有优劣。</p>
<p>例如MyISAM使用前缀压缩技术使得索引更小，但InnoDB则按照原数据格式进行存储。</p>
<p><img src="http://river.marvelthanos.com/blog/20200326/IgbxTjYbF5C5.png?imageslim" alt="mark"></p>
<h4 id="B-树索引的查询类型"><a href="#B-树索引的查询类型" class="headerlink" title="B+树索引的查询类型"></a>B+树索引的查询类型</h4><h5 id="全值匹配"><a href="#全值匹配" class="headerlink" title="全值匹配"></a>全值匹配</h5><h5 id="匹配最左前级"><a href="#匹配最左前级" class="headerlink" title="匹配最左前级"></a>匹配最左前级</h5><p>主要指两个方面联合索引可以使用最左的一部分索引进行匹配，还有就是单个索引前面某几个字段进行匹配 <code>LIKE &#39;P%&#39;</code></p>
<h5 id="匹配范围值，精确匹配呆一列并范围匹配另一列，只访问索引的查询（索引覆盖）"><a href="#匹配范围值，精确匹配呆一列并范围匹配另一列，只访问索引的查询（索引覆盖）" class="headerlink" title="匹配范围值，精确匹配呆一列并范围匹配另一列，只访问索引的查询（索引覆盖）"></a>匹配范围值，精确匹配呆一列并范围匹配另一列，只访问索引的查询（索引覆盖）</h5><p>因为索引树中的节点是有序的，所以除了按值查找之外，索引还可以用于查询中的ORDERBY操作（按顺序查找）。一般来说，如果B-Tree可以按照某种方式查找到值，那么也可以按照这种方式用于排序。</p>
<h3 id="B-Tree索引的限制"><a href="#B-Tree索引的限制" class="headerlink" title="B-Tree索引的限制"></a>B-Tree索引的限制</h3><p>假设现有索引 idx(a,b,c)</p>
<p>1.如果不是按照索引的最左列开始查找，则无法使用索引</p>
<p>条件值一定要是索引的最左部分，a=1或者a=1 and b=1 如果直接查询b=1或者b=1 and c=1 无法使用索引。</p>
<blockquote>
<p>如果查询条件是b=1 and c=1 and a=1虽然理论上不符合最左匹配，但实际上MySQL优化器会帮我们调整顺序来匹配索引，因此只要条件有最左前缀的列就行了，顺序不影响</p>
</blockquote>
<p>2.不能跳过索引中的列</p>
<p>如果查询a=1 and c=9 跳过了b列，只有a能使用索引，c列无法使用索引</p>
<p>3.如果查询中有某个列的范围查询，则其右边所有列都无法使用索引优化查找。</p>
<p>注意：这个范围查询的列本身还是能用到索引的，只是它右边开始的列用不到，LIKE也属于范围查询。</p>
<h3 id="为什么是B-Tree"><a href="#为什么是B-Tree" class="headerlink" title="为什么是B-Tree"></a>为什么是B-Tree</h3><p>假设我是数据库设计者要存放数据首先想到的肯定是</p>
<p><strong>数组</strong></p>
<p>数组最大的问题查找性能是O(N)那怎么改进呢</p>
<p><strong>有序数组</strong></p>
<p>有序数组在<strong>等值查询和范围查询</strong>场景中的性能都非常优秀。用二分法就可以快速找到（时间复杂度为O(logN)）。但是如果要往中间插入一条数据，则必须挪动后面的所有记录，成本较高。因此，有序数组只适用于<strong>静态存储引擎</strong>，即数据表一旦建立后不再会修改。继续改进</p>
<p><strong>二叉查找树</strong></p>
<p>查找树的查询效率很大程度取决于树的形状因此可以选用</p>
<p><strong>平衡二叉树(AVL树)</strong></p>
<p>任意节点的两个子树的高度最大差为1，但是这样树的高度还是太高了，每次查找的次数还是太多，继续改进</p>
<p><strong>多叉平衡搜索树</strong></p>
<p>为了减少查找次数，每次查找尽量拿出更多的节点，因此就要用到多叉树。</p>
<p>到这里就是索引最终的形态，我们的B树其实就是属于一颗多叉平衡搜索树</p>
<h3 id="B树和B-树"><a href="#B树和B-树" class="headerlink" title="B树和B+树"></a>B树和B+树</h3><p>B+树实际上属于B树的一个变体，</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/jRw1ov2Bt1Vp.png?imageslim" alt="mark"></p>
<p><img src="http://river.marvelthanos.com/blog/20200327/tUI2bii30YtD.png?imageslim" alt="mark"></p>
<h4 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h4><p>B树的非叶子节点也存放数据，B+树非叶子节点不存放数据</p>
<p>B树的叶子节点间没有指针相连，B+树叶子节点间有指针相连</p>
<h4 id="影响"><a href="#影响" class="headerlink" title="影响"></a>影响</h4><p>B树的节点也存放数据，找到索引就是找到数据，查询的速度不稳定最快的是O(1)</p>
<p>B+树节点不存放数据，数据都放在叶子节点，因此查询<strong>速度稳定</strong></p>
<p>B+树节点不存放数据因此每页能存放更多的节点，这样的好处就是树的<strong>高度更矮</strong></p>
<p>B+树叶子节点间增加了指针相连，<strong>更适合数据遍历</strong></p>
<h3 id="为什么Mongodb索引用B树，而Mysql用B-树"><a href="#为什么Mongodb索引用B树，而Mysql用B-树" class="headerlink" title="为什么Mongodb索引用B树，而Mysql用B+树?"></a><a href="https://blog.csdn.net/fujiandiyi008/article/details/104351945" target="_blank" rel="noopener">为什么Mongodb索引用B树，而Mysql用B+树?</a></h3><p>前面说了这么多其实不代表B树一无是处，只是适用于不同的场景，如MongoDB就使用了B树作为索引。</p>
<p>原因</p>
<p>B+树适合范围查询，MySQL是关系型数据，很多时候我们需要join不同的表来拼凑出最终的结果，而JOIN的过程其实就是一个<strong>数据遍历</strong>的过程</p>
<p>而MongoDB是非关系型数据库，都是一次查询为主，join表也是不怎么提倡的，使用B树平均性能更好，因此就使用B树。</p>
<p>当然了，MongoDB用B树也不会这么傻把数据整份存放在节点上，实际上也只是多存放了一个指向数据的指针。</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/V7QuM8OCeWIT.png?imageslim" alt="mark"></p>
<h2 id="聚簇索引非聚簇索引"><a href="#聚簇索引非聚簇索引" class="headerlink" title="聚簇索引非聚簇索引"></a>聚簇索引非聚簇索引</h2><p>InnoDB和MyISAM虽然都用B+树作为引擎，但是他们的实现形式就大不相同了。</p>
<p>之前说过B+树的数据都存放在叶子节点，这个数据不一定指的就是行数据，这里引入一个概念，聚簇和非聚簇</p>
<p>简单来说</p>
<p>聚簇索引：叶子节点上存放的就是真实的行数据，找到索引就是找到数据。<br>非聚簇索引：将实际数据存储与索引分开结构，索引的叶子节点存放数据的位置，需要再根据这个位置找到真正的数据。</p>
<p><strong>MyISAM</strong>主要使用的是非聚簇索引，通过key_buffer把索引先缓存到内存中，当需要访问数据时（通过索引访问数据），在内存中直接搜索索引，然后通过索引找到磁盘相应数据。</p>
<p>也因此MyISAM的表分开两个文件存放</p>
<p>*.MYD–”D”数据信息文件，是表的数据文件。<br>*.MYI–”I”索引信息文件，是表数据文件中任何索引的数据树。</p>
<blockquote>
<p>MySQL 8.0之后无论InnoDB还是MyISAM都没有*.frm来存放表结构定义了</p>
<p>元数据都存在系统表空间里</p>
</blockquote>
<p>InnoDB混合了聚簇索引和非聚簇索引。</p>
<p>通过聚簇索引（通常是主键索引）组织存储数据，索引即数据，因此InnoDB引擎的数据通常只有一个文件*.ibd</p>
<p>一般除主键之外的索引都是非聚集索引，也叫辅助索引，非聚集索引的叶子节点存放的不是行数据而是<strong>主键</strong>，因此访问数据时需要再利用这个主键去聚集索引进行<strong>二次查找</strong>。</p>
<p>聚簇索引默认是<strong>主键</strong>，但不代表<strong>聚簇索引一定是主键</strong>，或者主键一定是聚簇索引。</p>
<p>因为如果表中没有定义主键，InnoDB 会选择一个<strong>唯一的非空索引</strong>代替。如果也没有这样的索引，InnoDB 会隐式定义一个主键来作为聚簇索引。</p>
<blockquote>
<p>实际上InnoDB每行数据都有一个叫RowId的隐藏列，一旦实在找不到主键，这一列就不为空，并以此作为聚簇索引</p>
</blockquote>
<p><img src="http://river.marvelthanos.com/blog/20200327/OfJTERwzQnvu.png?imageslim" alt="mark"></p>
<p>可以看到InnoDB数据都挂在聚簇索引的叶子节点上，如果要通过辅助索引查找值就要先查到主键值然后再在聚簇索引上进行二次查找，从而找到实际数据。</p>
<p>而MyISAM无论主键索引还是辅助索引都是在叶子节点上直接存放“行指针“，两者并没有什么明显的区别。</p>
<p>“InnoDB 只聚集在同一个页面中的记录。包含相邻健值的页面可能相距甚远。“</p>
<p>书上有一句这样的话，按我的理解这里主要指的是叶子节点上数据页的聚集情况，一次读取一页数据，这一页数据是聚集到一起了，但是相邻键值的数据可能在另外一页，页与页之间的距离是不固定的，要看硬盘的实际存储情况。</p>
<h4 id="为什么辅助索引使用主键作为”指针”而不是使用地址值作为指针"><a href="#为什么辅助索引使用主键作为”指针”而不是使用地址值作为指针" class="headerlink" title="为什么辅助索引使用主键作为”指针”而不是使用地址值作为指针"></a>为什么辅助索引使用主键作为”指针”而不是使用地址值作为指针</h4><p>因为这样减少了当出现行移动或者数据页分裂时辅助索引的维护工作，使用主键值当作指针会让辅助索引占用更多的空间，换来的好处是InnoDB在移动行时无须更新辅助索引中的这个”指针”。也就是说行的位置（实现中通过16K的Page来定位）会随着数据库里数据的修改而发生变化（前面的B+树节点分裂以及Page的分裂），使用聚簇索引就可以保证不管这个主键B+树的节点如何变化，辅助索引树都不受影响。</p>
<h3 id="聚簇索引的优势"><a href="#聚簇索引的优势" class="headerlink" title="聚簇索引的优势"></a>聚簇索引的优势</h3><ol>
<li>索引即数据，通过聚簇索引的找到叶子节点就是数据所在比在非聚簇索引上快。</li>
<li>由于行数据和叶子节点存储在一起，<strong>同一页中会有多条行数据</strong>，访问同一数据页不同行记录时，已经把页加载到了Buffer中，再次访问的时候，会在内存中完成访问，不必访问磁盘。这样主键和行数据是一起被载入内存的，找到叶子节点就可以立刻将行数据返回了，如果按照主键Id来组织数据，获得数据更快。</li>
<li>因为叶子节点上的数据本身就是有序的，如果order by 命中聚簇索引就不用再另外排序，适合排序的场景。</li>
<li>叶子节点上的数据本身就是有序的而且一页可以存放多条数据，当需要取出一定范围数据的时候需要访问的io次数也就少了很多。</li>
</ol>
<h3 id="聚簇索引的劣势"><a href="#聚簇索引的劣势" class="headerlink" title="聚簇索引的劣势"></a>聚簇索引的劣势</h3><ol>
<li><p>聚簇索引主要是提高IO密集型应用的性能，如果数据全部都在内存，那聚簇索引就没什么优势了。</p>
</li>
<li><p>聚簇索引以外的辅助索引需要经过二次查找。</p>
</li>
<li><p>因为辅助索引的叶子节点存放的是聚簇索引的主键，过长的主键值，会导致辅助索引叶子节点占用更多的物理空间。</p>
</li>
<li><p><strong>维护索引很昂贵</strong>，聚簇索引上的数据是按顺序存放的，虽然按索引顺序读和取范围数据的时候很爽，但是这也意味着插入数据的时候要按序插入，所以语句插入的顺序尽量按聚集索引的顺序，否则就要频繁将数据移到合适的位置，MyISAM使用非聚簇索引索引上只是行指针，数据可以随意存放。</p>
</li>
<li><p><strong>空间碎片。</strong>当不按索引顺序插入新行或者主键要更新的时候，可能要往一个已经写满的页上继续插入新数据，这就导致要进行页分裂(page split)，用两页来存放，新一页可能只有一条数据（因为是链表形式相连，因此后面的数据并不会自动挪到新页上，也没必要），这就导致空间浪费也就是空间碎片了。</p>
</li>
<li><p><strong>全表扫描变慢了</strong>当频繁发生页分裂的时候，除了空间浪费也会使数据存储稀疏，比如原来一页可以存16条数据，极端情况可能就要16页来存这16条数据，全表扫描的时候就要进行16次IO操作。</p>
</li>
</ol>
<p>举例，如果按主键索引顺序插入数据</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/WUxLdu1kWEa6.png?imageslim" alt="mark"></p>
<p>但是如果使用UUID作为主键聚簇索引，因为新行的主键不一定比之前插入的大，无法简单地把新行插入到索引最后，那么数据插入的位置就是近乎随机的，</p>
<p>通常是已有数据的中间位置。</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/rPsR8IvyltNf.png?imageslim" alt="mark"></p>
<p>对于空间碎片和页分裂的问题，建议在大量插入新行后，选在负载较低的时间段，通过</p>
<p><code>OPTIMIZE TABLE</code>优化表，这会让数据重新分布，把未使用的空间碎片也是用上，但是这个操作会锁表。</p>
<h3 id="什么时候适合"><a href="#什么时候适合" class="headerlink" title="什么时候适合"></a>什么时候适合</h3><p>涉及到大数据量的非索引排序、全表扫描、count之类的操作的话，还是MyISAM占优势些，因为索引所占空间小，这些操作是需要在内存中完成的。</p>
<p>列要不要建索引，要建哪种索引</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/QQT78qpg63aa.png?imageslim" alt="mark"></p>
<p>聚簇索引性能最好而且具有唯一性，而且目前为止InnoDB还不支持指定聚簇索引，因此必须慎重设置。<strong>一般要根据这个表最常用的SQL查询方式来进行选择，某个字段作为聚簇索引，或组合聚簇索引</strong>。</p>
<h2 id="索引的存储"><a href="#索引的存储" class="headerlink" title="索引的存储"></a>索引的存储</h2><p><a href="https://blog.csdn.net/sinat_14913533/article/details/104855311" target="_blank" rel="noopener">Innodb的B+树能存多少数据</a></p>
<p><a href="https://baijiahao.baidu.com/s?id=1644888989326529522" target="_blank" rel="noopener">1条记录在表中是如何存储的</a></p>
<p>了解索引及数据行的实际存储格式有利于我们理解索引，这里主要介绍InnoDB,在InnoDB的最小的存储单元叫页（page），一页占16k，可以通过<code>innodb_page_size</code>来设置。</p>
<p>注意这里的“页”是InnoDB自己起的一个概念，跟内存中的页是不一样的，他跟磁盘的关系大概是这样</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/Unv45iNgvH1j.png?imageslim" alt="mark"></p>
<p>磁盘最小的储存单元是扇区512bytes，而在一些文件系统如NTFS最少的存储单元是（块）4096bytes也就是8个扇区，再到InnoDB层就是16k=32个扇区</p>
<p>也就是说InnoDB无论是查找数据还是查找索引，每次起码要读32个扇区也就是16k</p>
<p><strong>无论是索引内部节点还是叶子节点都是16k</strong>，如果一条记录长度大于16k就分多页来存</p>
<p><img src="http://river.marvelthanos.com/blog/20200327/OFSEM8DDCANx.png?imageslim" alt="mark"></p>
<h2 id="索引下推"><a href="#索引下推" class="headerlink" title="索引下推"></a>索引下推</h2><p>索引下推（Index Condition Pushdown，ICP）顾名思义就是把查询条件下推到存储引擎层，让存储引擎做一部分筛选，减少回表次数</p>
<p>假设有这么个需求，查询表中“名字第一个字是张，性别男，年龄为10岁的所有记录”。那么，查询语句是这么写的：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from tuser where name like &#39;张%&#39; and age&#x3D;10 and ismale&#x3D;1;</span><br></pre></td></tr></table></figure>

<p>根据前面说的“最左前缀原则”，该语句在搜索索引树的时候，<strong>只能</strong>匹配到名字第一个字是‘张’的记录（即记录ID3），接下来是怎么处理的呢？当然就是从ID3开始，逐个回表，到主键索引上找出相应的记录，再比对age和ismale这两个字段的值是否符合。</p>
<p>但是！MySQL 5.6引入了索引下推优化，<strong>可以在索引遍历过程中，对索引中包含的字段先做判断，过滤掉不符合条件的记录，减少回表字数</strong>。</p>
<p>下面图1、图2分别展示这两种情况。</p>
<p><img src="http://river.marvelthanos.com/blog/20200330/7pl4ukAAypjt.png?imageslim" alt="mark"></p>
<p><img src="http://river.marvelthanos.com/blog/20200330/djb38wpxrrMh.png?imageslim" alt="mark"></p>
<p>图 1 中，在 (name,age) 索引里面我特意去掉了 age 的值，<strong>这个过程 InnoDB 并不会去看 age 的值</strong>，只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此，需要回表 4 次。</p>
<p>图 2 跟图 1 的区别是，InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10，对于不等于 10 的记录，直接判断并跳过。在我们的这个例子中，只需要对 ID4、ID5 这两条记录回表取数据判断，就只需要回表 2 次。</p>
<p>注意点：<br> 1、innodb引擎的表，索引下推只能用于二级索引。</p>
<blockquote>
<p>就像之前提到的，innodb的主键索引树叶子结点上保存的是全行数据，所以这个时候索引下推并不会起到减少查询全行数据的效果。</p>
</blockquote>
<p>2、索引下推一般可用于所求查询字段（select列）不是/不全是联合索引的字段，查询条件为多条件查询且查询条件子句（where/order by）字段全是联合索引。</p>
<blockquote>
<p>假设表t有联合索引（a,b）,下面语句可以使用索引下推提高效率<br> select * from t where a &gt; 2 and b &gt; 10;</p>
</blockquote>
<p>当使用索引下推时Extra字段显示“Using index condition”</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>《高性能MySQL（第3版）》</p>
<p>《MySQL技术内幕InnoDB存储引擎第2版》</p>
<p><a href="https://blog.csdn.net/fujiandiyi008/article/details/104351945" target="_blank" rel="noopener">为什么Mongodb索引用B树，而Mysql用B+树?</a></p>
<p><a href="https://www.jianshu.com/p/fa8192853184" target="_blank" rel="noopener">聚簇索引与非聚簇索引（也叫二级索引）</a></p>
<p><a href="https://baijiahao.baidu.com/s?id=1645514817836645220" target="_blank" rel="noopener">详细聊聊MySQL中 聚簇、非聚簇索引和覆盖索引</a></p>
<p><a href="https://www.cnblogs.com/kkbill/p/11354685.html" target="_blank" rel="noopener">MySQL索引解析（联合索引/最左前缀/覆盖索引/索引下推）</a></p>
<p><a href="https://www.jianshu.com/p/bdc9e57ccf8b" target="_blank" rel="noopener">mysql索引篇之覆盖索引、联合索引、索引下推</a></p>

        
<script src="/blog/js/qrious.min.js"></script>


        <hr>
        <div>
            
            <p>
                <span>专题: </span>
                
                <a href="/blog/series/#MySQL">
                    <code key="MySQL" class="post-label">MySQL</code>
                </a>
                
            </p>
            
            <p>
                <span>本文发表于 2020-03-02，最后修改于 2020-03-31。</span>
            </p>
            <!-- 文章末尾的提示 start -->
            
            
            
                
            <!-- 文章末尾的提示 end -->
        </div>
        <hr />
        <p name="pagination" style="font-size: 1.2em">
    
    <a class="float-left" href="/blog/post/go-how-to-build.html">上一篇 « Golang程序编译执行流程</a>
    
    
    <a class="float-right" href="/blog/post/mysql-optimize-explain.html">下一篇 » EXPLAIN-执行计划查询命令</a>
    
</p>
    </article>
    <!-- 赞赏 -->
    <!--打赏-->

    <!-- 推荐阅读三篇文章 -->
    <div class="col-xs-12">
        <h3>推荐阅读</h3>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang底层"
                    title="Golang底层">Golang底层</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-how-to-build.html" class="title">
                Golang程序编译执行流程
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-03-01，</span>
                    <span class="hidden-xs">全文7487字，</span>
                    <span>阅读约25分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/tcp/tcp.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#基础"
                    title="基础">基础</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#tcp/http"
                    title="tcp/http">tcp/http</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/tcp-three-way-handshake.html" class="title">
                简述TCP“3次握手，4次挥手”
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-02-21，</span>
                    <span class="hidden-xs">全文9978字，</span>
                    <span>阅读约34分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang源码"
                    title="Golang源码">Golang源码</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-learn-interface.html" class="title">
                Go学习 接口和类型断言
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2019-01-21，</span>
                    <span class="hidden-xs">全文13117字，</span>
                    <span>阅读约44分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
    </div>
    <div class="col-xs-12">
        <!-- 标签列表 -->
        <!-- Featured Tags -->
<style>
    #featured-tag .post-tag-item {
        font-size: 12px;
        line-height: 30px;
        display: inline-block;
        height: 30px;
        margin: 5px 0px;
        padding: 0 7px;
        color: #333;
        border-radius: 15px;
        background: #f6f6f6;
    }

    #featured-tag .post-tag-item:hover {
        color: #337ab7;
    }
</style>
<div id="featured-tag">
    
    <a class="post-tag-item" href="/blog/tags/#关于我" title="关于我"
        rel="1">#关于我 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#友链" title="友链"
        rel="1">#友链 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang汇编" title="Golang汇编"
        rel="4">#Golang汇编 (4) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang底层" title="Golang底层"
        rel="5">#Golang底层 (5) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang" title="Golang"
        rel="8">#Golang (8) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang基础" title="Golang基础"
        rel="1">#Golang基础 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang源码" title="Golang源码"
        rel="2">#Golang源码 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#基础" title="基础"
        rel="2">#基础 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#tcp/http" title="tcp/http"
        rel="2">#tcp/http (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL" title="MySQL"
        rel="3">#MySQL (3) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL优化" title="MySQL优化"
        rel="1">#MySQL优化 (1) </a>
    
</div>
    </div>
    <!-- 评论 -->
    <div class="col-xs-12">
        
    </div>
</div>
<aside class="float-left gkt-sidebar hidden-xs hidden-sm">
    <div style="clear: both"></div>
    <div class="gkt-sidebar-wrapper">
        <section class="box-shadow"><style>
    .gkt-summary {
        border: 1px solid #DDDDDD;
        border-radius: 3px;
        padding: 5px;
        width: 100%;
    }


    .gkt-summary nav {
        overflow: hidden;
    }

    .gkt-summary nav a {
        display: inline-block;
        text-align: center;
        color: #333;
        font-size: 12px;
    }

    .gkt-summary nav span {
        display: block;
    }

    .gkt-summary nav .middle {
        border-left: 1px solid #eaecef;
        border-right: 1px solid #eaecef;
    }

    .gkt-summary .number {
        font-weight: bold;
    }

    .gkt-summary .link-list {
        margin-top: 5px;
        margin-bottom: -5px;
        padding-top: 7px;
        border-top: 1px dashed #999;
        display: flex;
    }

    .gkt-summary .link-list a {
        flex: 1;
    }

    .gkt-summary .link-list img {
        width: 25px;
        height: 25px;
    }
</style>

<div class="gkt-summary">
    <nav>
        <a href="/blog/" class="col-xs-4">
            <number class="number">15</number><span>文章</span>
        </a>
        <a href="/blog/series" class="col-xs-4 middle">
            <number class="number">5</number><span>专题</span>
        </a>
        <a href="/blog/tags" class="col-xs-4">
            <number class="number">11</number><span>标签</span>
        </a>
    </nav>

    
    <div class="link-list">
        
        <a href="https://marvelthanos.com" target="_blank" rel="noopener"><img src="/blog/img/related_links/thanos.webp" /></a>
        
    </div>
    
</div></section>
        
        
        <section class="gkt-sidebar-content box-shadow">
            <strong>MySQL</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/mysql-index-use.html"
                        class="">《高性能MySQL》笔记-索引优化</a>
                    
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html"
                        class="gkt-sidebar-active">《高性能MySQL》笔记-索引基础</a>
                    
                    <!-- Table of Contents -->
<div id="sidebar-toc">
  <!-- TOC  -->
  
  <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#索引简介"><span class="toc-nav-text">索引简介</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#索引的类型"><span class="toc-nav-text">索引的类型</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#聚簇索引非聚簇索引"><span class="toc-nav-text">聚簇索引非聚簇索引</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#索引的存储"><span class="toc-nav-text">索引的存储</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#索引下推"><span class="toc-nav-text">索引下推</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#参考资料"><span class="toc-nav-text">参考资料</span></a></li></ol>
  
</div>

<script>
  (function () {
    var h2 = document.querySelectorAll('article h2');
    var h3 = document.querySelectorAll('article h3');
    var linkList = document.querySelectorAll('#sidebar-toc a');

    function findLinkElement(name) {
      for (var i = 0; i < linkList.length; i++) {
        var items = linkList[i].href.split('#');
        if (items && items[items.length - 1] === encodeURIComponent(name)) {
          return i;
        }
      }
      return -1;
    }

    function activeLink(titleList) {
      var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
      for (var i = titleList.length - 1; i >= 0; i--) {
        if (scrollTop - titleList[i].offsetTop > 0) {
          var index = findLinkElement(titleList[i].id);
          index != -1 && linkList[index].classList.add('gkt-sidebar-active');
          break;
        }
      }
    }

    window.addEventListener("scroll", function (e) {
      [].slice.call(linkList).forEach(function (link) {
        link.classList.remove('gkt-sidebar-active');
      })
      activeLink(h2);
    })
  })();
</script>
                    
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html"
                        class="">EXPLAIN-执行计划查询命令</a>
                    
                </li>
                
            </ul>
        </section>
        
        
        <section class="box-shadow">
            <strong>最近的文章</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-learn-lib-sorts.html">Go源码学习 sort包</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html">EXPLAIN-执行计划查询命令</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html">《高性能MySQL》笔记-索引基础</a>
                </li>
                
            </ul>
        </section>
        <section class="box-shadow text-center">
            
        </section>
    </div>
</aside>

<script>
    (function () {
        var ele = getDom('.gkt-sidebar-content')
        var wrapper = getDom('.gkt-sidebar-wrapper')
        var last = 0
        window.addEventListener("scroll", function (e) {
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            var isDown = scrollTop > last;
            var pos = getPosition(ele).y - scrollTop;
            var downLimit = 50;
            var upLimit = -100;
            // uarrow.style.marginTop = scrollTop + 'px';
            isDown && pos <= downLimit && wrapper.classList.add("gkt-sidebar-fixed");
            !isDown && pos > upLimit && wrapper.classList.remove("gkt-sidebar-fixed");
            last = scrollTop
        })
    })();
    (function () {
        function resizeUArrow() {
            var s = getDom('.u-arrow-wrapper').style
            var pc = getDom('.post-container')
            s.left = getPosition(pc).x + 'px';
            s.width = pc.clientWidth + 'px';
        }
        resizeUArrow()
        window.addEventListener('resize', resizeUArrow);
    })();
</script>

    
</main>
    </div>
    <style>
    img#go-top {
        position: fixed;
        bottom: 100px;
        width: 50px;
        cursor: pointer;
        z-index: 9999;
    }
</style>
<img id="go-top" src="/blog/icon/top.png" class="hidden-xs" style="display: none" />
<script>
    (function () {
        var goTop = document.getElementById('go-top');
        var mainContainer = document.querySelector('.main-container');
        
        goTop.addEventListener('click', function () {
            window.scroll(0, 0);
        }, false);
        window.addEventListener('scroll', function () {
            var right = document.body.offsetWidth - mainContainer.getBoundingClientRect().right;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            goTop.style.right = right + 10 + 'px'
            scrollTop > 700 && (goTop.style.display = "block");
            scrollTop <= 700 && (goTop.style.display = "none");
        });
    })();
</script>
    <style>
    #geektutu-click-img-container {
        position: fixed;
        left: 0;
        top: 0;
        text-align: center;
        width: 100%;
        display: none;
        z-index: 9999;
    }

    #geektutu-click-img-container img {
        object-fit: contain;
        background: #eaecef;
        padding: 15px;
        border-radius: 10px;
        height: auto;
        width: auto;
        vertical-align: middle;
    }
</style>


<div id="geektutu-click-img-container">
    <img src="" alt="Big Image">
</div>

<script>
    (function () {
        var container = document.querySelector('#geektutu-click-img-container')
        var targetImg = container.querySelector('img')
        var imgs = document.querySelectorAll('article img');
        targetImg.addEventListener('click', function (e) {
            container.style.display = 'none';
            e && e.stopPropagation && e.stopPropagation();
        }, false);

        for (var i = 0; i < imgs.length; ++i) {
            var img = imgs[i];
            img.addEventListener('click', (function (src, rate) {
                return function (e) {
                    e && e.stopPropagation && e.stopPropagation();
                    if (window.innerWidth < 980) {
                        return
                    }
                    targetImg.style.height = targetImg.style.width = 'auto';
                    if (window.innerWidth / window.innerHeight > rate) {
                        targetImg.style.height = (window.innerHeight - 20) + 'px';
                    } else {
                        targetImg.style.width = (window.innerWidth - 20) + 'px';
                    }
                    container.style.height = window.innerHeight + 'px'
                    container.style.lineHeight = window.innerHeight + 'px'
                    container.style.display = 'block';
                    targetImg.src = src;
                };
            }(img.src, img.width / img.height)), false)
        }
    })();
</script>
    <!-- Footer -->
    <!-- Footer -->
<style>
    footer {
        width: 100%;
        line-height: 1.5;
        padding: 20px;
    }

    footer a {
        color: #333;
        text-decoration: none;
    }

    .footer-hexo img {
        height: 20px;
        margin-bottom: -5px;
    }

    .footer-hexo a {
        color: #337ab7;
    }
</style>
<footer class="text-center col-xs-12">
    <p>
        <small>© 2020 - Evrn - </small>
        <small>
            <a target="_blank" rel="nofollow noopener" href="http://www.miitbeian.gov.cn/"></a>
        </small>
    </p>
    <p class="footer-hexo">
        <!-- 但若直接使用或修改主题，请务必保留这段声明 -->
        <small>Powered by <a target="_blank" href="https://hexo.io">Hexo</a> | Theme
            <a target="_blank" href="https://geektutu.com">Geektutu</a>
            <a href="https://github.com/geektutu/hexo-theme-geektutu" target="_blank" rel="noopener">
                <img src=""
                    alt="Github Star">
            </a>
        </small>
    </p>
    
    <p>
        <small>
            <span id="busuanzi_container_site_pv">👁<span id="busuanzi_value_site_pv"></span></span> &nbsp;
            <span id="busuanzi_container_page_pv">📚<span id="busuanzi_value_page_pv"></span></span>
        </small>
    </p>
    
    

</footer>


<script>
    window.addEventListener('load', function () {
        globalAddScript('//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js');
    })
</script>

<!-- Baidu Tongji -->





    <script>
        // 非本站网址，新开tab页签
        (function () {
            var stopBubble = function (e) {
                e && e.stopPropagation && e.stopPropagation();
            }
            var links = document.querySelectorAll('a');
            [].slice.call(links).forEach(function (item) {
                if (item.href && item.href.indexOf(window.location.host) === -1) {
                    item.target = '_blank'
                }
                // 阻止冒泡，不触发彩蛋。
                item.addEventListener('click', stopBubble, false);
            });
            var article = document.querySelector('article');
            article && article.addEventListener('click', stopBubble, false)
        })();
    </script>
    
</body>

</html>